/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved.
 *
 */

import path from 'path';

import { noop } from '@baize/wdk';
import { coreParameter, FileSet, MetricLogType, TaskDetails, TaskInputValue } from '@ohos/hvigor';
import { DIRECT_IMPORTEE_CACHE_FILE_NAME } from '@ohos/hvigor-arkts-compose';
import { OHOS_ARK_COMPILE_SOURCE_MAP_DIR } from '@ohos/hvigor/src/common/const/const';
import { HvigorConfigLoader } from '@ohos/hvigor/src/common/util/hvigor-config-loader';
import * as fse from 'fs-extra';

import { PackingToolOptions } from '../../builder/inner-java-command-builder/packing-tool-options.js';
import { projectOhosConfigManager } from '../../common/global/project-ohos-config-manager.js';
import { PathInfo } from '../../common/path-info.js';
import { BuildArtifactConst, BuildArtifactExtension, BuildDirConst } from '../../const/build-directory-const.js';
import { CommonConst, CommonNodeConst, DefaultTargetConst, HvigorConfigConst } from '../../const/common-const.js';
import { ToolChainsConst } from '../../const/sdk-const.js';
import { AotCompileModeEnum } from '../../enum/aot-compile-mode-enum.js';
import { CodeType } from '../../enum/code-type-enum.js';
import { ModuleType } from '../../enum/module-type-enum.js';
import { ModuleModelImpl } from '../../model/module/module-model-impl.js';
import { BuildOpt } from '../../options/build/build-opt.js';
import { ModuleBuildProfile } from '../../options/build/module-build-profile.js';
import { Dependency, DependencyType } from '../../project/dependency/core/dependency-interface.js';
import { DependencyManager } from '../../project/dependency/dependency-manager.js';
import { InjectUtil } from '../../utils/inject-util.js';
import {generateIntegratedHspCommand, getAppInfoByProject} from '../../utils/integrated-hsp-utils.js';
import { OhosLogger } from '../../utils/log/ohos-logger.js';
import { ProcessUtils } from '../../utils/process-utils.js';
import { overWriteSourceMap } from '../../utils/sourcemap-util.js';
import { doObfuscationCheck } from '../../utils/task-util.js';
import { INPUT_KEY } from '../abstract-merge-profile.js';
import { CacheNativeLibs } from '../cache-native-libs.js';
import { TaskNames } from '../common/task-names.js';
import { GenerateMetadata } from '../generate-metadata.js';
import { MakePackInfo } from '../make-pack-info.js';
import { sendPackRequest, PackParams } from '../pack/pack-hap-request.js';
import { ProcessIntegratedHsp } from '../process-integrated-hsp.js';
import { ProcessRouterMap } from '../process-router-map.js';
import { ProcessStartupConfig } from '../process-startup-config.js';
import { ModuleTaskService } from '../service/module-task-service.js';
import { TargetTaskService } from '../service/target-task-service.js';
import { SyscapTransform } from '../syscap-transform.js';
import { OhosHapTask } from '../task/ohos-hap-task.js';
import { getJson5Obj } from '../../utils/json-util.js';

const ohosTestTargetName = DefaultTargetConst.OHOS_TEST_TARGET;

/**
 * Stage模型打包Hap包的基类
 *
 * @since 2023/1/17
 */
export abstract class BasePackHapTask extends OhosHapTask {
  protected readonly mode: string;
  protected readonly libPath: string;
  protected readonly jsonPath: string;
  protected readonly packageHapJsonPath: string;
  protected readonly resourcePath: string;
  protected readonly indexPath: string;
  protected readonly rpcidSc: string;
  protected readonly jsAssetsPath: string;
  protected readonly etsAssetsPath: string;
  protected readonly nodeModulesPath: string;
  protected readonly allPackInfoPath: string[] = [];
  protected readonly allOutPath: string[] = [];
  protected readonly sourceMapInterDirPath: string;
  protected syscapJsonPath: string;
  protected pkgContextInfoPath: string;
  protected readonly fastAppDirPath: string;

  // 声明与aot相关的变量
  private readonly anBuildOutputPath: string;
  private readonly apDirPath: string;
  private moduleBuildOpt: ModuleBuildProfile.ModuleBuildOpt;
  private readonly buildOption?: BuildOpt;
  private readonly integratedHsp: boolean;
  readonly _log: OhosLogger = OhosLogger.getLoggerWithDurationId('base-pack-hap-task', this.durationEvent.getId());

  declareExecutionTool(): string {
    return this.sdkInfo.getPackageTool();
  }

  declareExecutionCommand(): string {
    const allCommand: string[] = [];
    for (let i = 0; i < this.allPackInfoPath.length; i++) {
      allCommand.push(this.generateCommand(this.allPackInfoPath[i], this.allOutPath[i]).toString());
    }
    return allCommand.join();
  }

  declareInputFiles(): FileSet {
    const fileSet = new FileSet().addEntries([this.libPath, this.jsonPath, this.resourcePath, this.indexPath, ...this.allPackInfoPath], { isDirectory: false });

    if (fse.existsSync(this.rpcidSc)) {
      fileSet.addEntry(this.rpcidSc, { isDirectory: false });
    }

    if (fse.existsSync(this.jsAssetsPath)) {
      fileSet.addEntry(this.jsAssetsPath, { isDirectory: false });
    }

    if (fse.existsSync(this.etsAssetsPath)) {
      fileSet.addEntry(this.etsAssetsPath, { isDirectory: false });
    }

    if (fse.existsSync(this.nodeModulesPath)) {
      fileSet.addEntry(this.nodeModulesPath, { isDirectory: true });
    }

    if (fse.existsSync(this.pkgContextInfoPath)) {
      fileSet.addEntry(this.pkgContextInfoPath, { isDirectory: false });
    }

    const defaultSourceMapPath = this.getSourceMapSourcePath();
    if (fse.existsSync(defaultSourceMapPath)) {
      fileSet.addEntry(defaultSourceMapPath, { isDirectory: false });
    }

    // // 获取当前模块的buildProfile.ets文件 如果存在添加增量判断
    const targetName = this.targetService.getTargetData().getTargetName();
    const buildProfilePath = this.pathInfo.getGenerateBuildProfilePath(targetName);
    if (fse.existsSync(buildProfilePath)) {
      fileSet.addEntry(buildProfilePath);
    }
    return fileSet;
  }

  declareOutputFiles(): FileSet {
    const fileSet = new FileSet().addEntries([...this.allOutPath], { isDirectory: false });
    if (this.integratedHsp) {
      this.allOutPath.forEach((outPath) => {
        fileSet.addEntry(path.resolve(this.pathInfo.getModuleBuildOutputPath(), path.basename(outPath)), { isDirectory: false });
      });
    }
    if (this.sdkInfo.hasRollUpPluginInEtsLoader) {
      fileSet.addEntry(path.resolve(this.sourceMapInterDirPath, BuildArtifactConst.SOURCEMAPS_MAP));
      if (coreParameter.properties[OHOS_ARK_COMPILE_SOURCE_MAP_DIR]) {
        const sourceMapDirPath = this.getSourceMapPath(coreParameter.properties[OHOS_ARK_COMPILE_SOURCE_MAP_DIR]);
        fileSet.addEntry(path.resolve(sourceMapDirPath, this.moduleName, BuildArtifactConst.SOURCEMAPS_MAP), { isDirectory: false });
      } else {
        fileSet.addEntry(path.resolve(this.pathInfo.getDefaultSourceMapPath(), BuildArtifactConst.SOURCEMAPS_MAP));
      }
    }
    return fileSet;
  }

  protected constructor(taskService: TargetTaskService, detail: TaskDetails, mode: string, logger: OhosLogger) {
    super(taskService, detail);
    this.mode = mode;
    const targetData = this.targetService.getTargetData();
    const pathInfo: PathInfo = targetData.getPathInfo() as PathInfo;
    this.libPath = pathInfo.getIntermediatesStrippedLibsDir();
    this.jsonPath = path.resolve(pathInfo.getIntermediatesRes(), CommonConst.MODULE_JSON);
    this.packageHapJsonPath = path.resolve(this.pathInfo.getPackageHapProductPath(), CommonConst.MODULE_JSON);
    this.pkgContextInfoPath = path.resolve(this.pathInfo.getIntermediatesLoaderPath(), CommonConst.PKG_CONTEXT_INFO_JSON);
    this.resourcePath = path.resolve(pathInfo.getIntermediatesRes(), BuildDirConst.RESTOOL_BUILD_RESOURCES);
    this.indexPath = path.resolve(pathInfo.getIntermediatesRes(), BuildArtifactConst.RESOURCE_INDEX);
    this.rpcidSc = path.resolve(pathInfo.getIntermediatesSysCap(), CommonConst.RPCID_SC);
    this.jsAssetsPath = path.resolve(pathInfo.getInterMediatesLoaderOutPath(), CodeType.JS);
    this.etsAssetsPath = path.resolve(pathInfo.getInterMediatesLoaderOutPath(), CodeType.ETS);
    this.nodeModulesPath = path.resolve(pathInfo.getInterMediatesLoaderOutPath(), CommonNodeConst.NODE_MODULES);
    this.syscapJsonPath = path.resolve(this.moduleModel?.getSourceRootByTargetName(this.targetName), CommonConst.SYSCAP_JSON);
    this.sourceMapInterDirPath = pathInfo.getInterMediatesSourceMapDirPath();
    this.fastAppDirPath = pathInfo.getFastAppStorageDirectory();

    // 定义与aot相关的变量
    this.anBuildOutputPath = path.resolve(this.pathInfo.getInterMediatesLoaderOutPath(), AotCompileModeEnum.AN);
    this.moduleBuildOpt = this.moduleModel.getProfileOpt();
    this.buildOption = this.targetService.getBuildOption();
    this.apDirPath = this.pathInfo.getIntermediatesApPath();
    this.integratedHsp = this.moduleModel.isHspModule() && !!this.targetService.getBuildOption().arkOptions?.integratedHsp;
    // 打包hsp时如果hsp为集成态hsp则先打包到outputs/integrated_hsp目录下，然后调用回填工具回填到outputs目录下
    const outPath = this.integratedHsp ? pathInfo.getIntegratedHspOutputPath() : pathInfo.getModuleBuildOutputPath();

    if (!fse.existsSync(outPath)) {
      fse.mkdirSync(outPath, { recursive: true });
    }

    const relatedEntryModules = this.service.getRelatedEntryModules();
    const moduleType = this.service.getModuleModel().getModuleType();
    if (ModuleTaskService.checkEntryModules(moduleType, relatedEntryModules)) {
      for (const entryName of relatedEntryModules!) {
        if (this.module.findModuleByName(entryName)) {
          this.allPackInfoPath.push(path.resolve(pathInfo.getModuleBuildOutputPath(), entryName, BuildArtifactConst.PACK_INFO));
          this.allOutPath.push(path.resolve(outPath, targetData.getModuleTargetOutputFileName(entryName, false)));
        }
      }
    } else {
      this.allPackInfoPath.push(path.resolve(pathInfo.getModuleBuildOutputPath(), BuildArtifactConst.PACK_INFO));
      this.allOutPath.push(path.resolve(outPath, targetData.getModuleTargetOutputFileName('', false, this.getSuffix())));
    }
  }

  declareInputs(): Map<string, TaskInputValue> {
    const inputMap = new Map<string, TaskInputValue>()
      .set('hotReload', InjectUtil.isHotReload())
      .set(BuildDirConst.INTEGRATED_HSP, this.integratedHsp)
      .set(INPUT_KEY.projectConfigAppOpt, JSON.stringify(projectOhosConfigManager.getOverrides()?.signingConfig));

    if (this.moduleModel.getModuleType() !== ModuleType.Har && this.buildOption?.debuggable) {
      inputMap.set('sourceMapDir', coreParameter.properties[OHOS_ARK_COMPILE_SOURCE_MAP_DIR] ?? '');
    }

    return inputMap;
  }

  protected async doTaskAction(): Promise<void> {
    for (let i = 0; i < this.allPackInfoPath.length; i++) {
      await this.executeCommandList(this.allPackInfoPath[i], this.allOutPath[i]);
    }

    // 下面两个对于操作sourceMap的函数不能调换顺序
    // 第一步将sourceMap从cache移动到Intermediate下，第二步将sourceMap从Intermediate移动到mapping下
    this.generateInterSourceMap();
    this.moveSourceMap();
    await this.replaceSourceMapInCoverageMode();
  }

  private async executeCommandList(packInfoPath: string, outPath: string): Promise<void> {
    const inputData = this.generatePackInputData(packInfoPath, outPath);
    try {
      await this.executePackInJavaDaemon(inputData);
    } catch (err: any) {
      this._log.debug(`java daemon remote pack failed: ${err?.message}. fallback to local java -jar pack`);
      // 远程打包报错回退到尝试用本地java -jar打包
      await this.executePackInWorkerPool(inputData);
    }
  }

  /**
   * 生成打包命令数据
   * @private
   */
  private generatePackInputData(packInfoPath: string, outPath: string) {
    const subTaskName = 'generate HAP packaging command';
    const subDurationEvent = this.durationEvent.createSubEvent(subTaskName, '');
    subDurationEvent.start();
    this.checkCompressNativeLib();
    const command: string[] = this.generateCommand(packInfoPath, outPath);
    this._log._printDebugCommand(this.sdkInfo.getPackageTool(), command);
    const inputData = {
      moduleName: this.moduleName,
      taskName: this.name,
      commandLine: command,
      integratedHspCommand: this.integratedHsp
        ? generateIntegratedHspCommand(this.projectModel, this.sdkInfo.getPackageTool(), outPath, this.pathInfo.getModuleBuildOutputPath())
        : undefined,
    };
    subDurationEvent.stop();
    subDurationEvent.setLog(subDurationEvent.getName(), MetricLogType.INFO);
    return inputData;
  }

  private async sendPackHapRequest(command?: string[]) {
    if (command) {
      const packParam: PackParams = {
        command: command.slice(command.indexOf(this.sdkInfo.getPackageTool()) + 1),
      };
      await sendPackRequest(this.sdkInfo.getPackageTool(), packParam);
    }
  }

  /**
   * 发送打包命令到java-daemon中执行打包
   * @private
   */
  private async executePackInJavaDaemon(inputData: {commandLine: string[], integratedHspCommand?: string[]}) {
    const subToolTaskName = 'submit HAP packaging task to java daemon server';
    const subToolDurationEvent = this.durationEvent.createSubEvent(subToolTaskName, 'Pack HAP in java daemon');
    subToolDurationEvent.start();
    try {
      await this.sendPackHapRequest(inputData.commandLine);
      await this.sendPackHapRequest(inputData.integratedHspCommand);
      subToolDurationEvent.setLog(subToolTaskName, MetricLogType.INFO);
    } catch(err: any) {
      subToolDurationEvent.setLog(subToolTaskName, MetricLogType.WARN);
      throw err;
    } finally {
      subToolDurationEvent.stop();
    }
  }

  /**
   * 发送打包命令到worker-pool中执行打包
   * @private
   */
  private async executePackInWorkerPool(inputData: {moduleName: string, taskName: string}) {
    const subToolTaskName = 'submit HAP packaging task to work pool';
    const subToolDurationEvent = this.durationEvent.createSubEvent(subToolTaskName, 'Pack HAP in work pool');
    subToolDurationEvent.start();

    await new ProcessUtils(inputData.moduleName, inputData.taskName).submitExecutionWithoutReturn(
      this,
      this.getWorkerPool(),
      inputData,
      noop,
      [],
      subToolDurationEvent,
    );

    subToolDurationEvent.stop();
    subToolDurationEvent.setLog(subToolTaskName, MetricLogType.INFO);
  }

  private checkCompressNativeLib() {
    const moduleModel = this.moduleModel as ModuleModelImpl;
    const pressLevel = HvigorConfigLoader.getInstance().getPropertiesConfigValue<string>(HvigorConfigConst.OHOS_PACK_COMPRESS_LEVEL);
    if (pressLevel && fse.existsSync(path.resolve(this.pathInfo.getModulePath(), 'libs'))) {
      const specificLevel = HvigorConfigConst.OHOS_PACK_COMPRESS_LEVEL_MAP.get(pressLevel);
      if (this.compileApiVersion < 12) {
        this._log.warn(`Invalid ohos.pack.compressLevel configuration, ohos.pack.compressLevel configuration 
      minimum requirement is api 12 version of the project, your project is api ${this.compileApiVersion} project`);
        return;
      }
      if (!specificLevel) {
        this._log.warn(`invalid ohos.pack.compress parameter:${pressLevel}
      ,It won't work, the packing will follow the fast compression level.`);
      }
    }
  }

  /**
   * 生成命令
   *
   * @param packInfoPath pack.info路径
   * @param outPath 输出路径
   * @returns 命令
   */
  private generateCommand(packInfoPath: string, outPath: string): string[] {
    const packageOptions = new PackingToolOptions();
    packageOptions.addCalledJarFile(this.sdkInfo.getPackageTool());
    packageOptions
      .addMode(this.mode)
      .force(true)
      .addLibPath(this.libPath)
      .addJsonPath(this.packageHapJsonPath)
      .addResourcesPath(this.resourcePath)
      .addIndexPath(this.indexPath)
      .addPackInfoPath(packInfoPath)
      .addOutPath(outPath);

    // 判断是否要添加压缩参数
    const pressLevel = HvigorConfigLoader.getInstance().getPropertiesConfigValue<string>(HvigorConfigConst.OHOS_PACK_COMPRESS_LEVEL);
    if (pressLevel && fse.existsSync(path.resolve(this.pathInfo.getModulePath(), 'libs'))) {
      packageOptions.addCompressLevel(pressLevel, this.sdkInfo.getSdkVersion());
    }

    if (this.targetService.getAnBuildMode()) {
      // 判断anPath和apPath是否存在，是的话添加an与ap路径
      if (fse.existsSync(this.anBuildOutputPath)) {
        packageOptions.addAnPath(this.anBuildOutputPath);
      }

      if (fse.existsSync(this.apDirPath)) {
        packageOptions.addDirList([this.apDirPath]);
      }
    }

    if (fse.existsSync(this.rpcidSc)) {
      packageOptions.addSysCapPath(this.rpcidSc);
    }

    if (fse.existsSync(this.jsAssetsPath)) {
      packageOptions.addJsPath(this.jsAssetsPath);
    }

    if (fse.existsSync(this.etsAssetsPath)) {
      packageOptions.addEtsPath(this.etsAssetsPath);
    }

    if (fse.existsSync(this.nodeModulesPath)) {
      packageOptions.addDirList([this.nodeModulesPath]);
    }

    if (fse.existsSync(this.pkgContextInfoPath) && this.targetService.getBuildOption()?.strictMode?.useNormalizedOHMUrl) {
      packageOptions.addPkgContextInfoPath(this.pkgContextInfoPath);
    }
    return packageOptions.build();
  }

  private getSuffix() {
    switch (this.mode) {
      case 'hap':
        return BuildArtifactExtension.DOT_HAP;
      case 'hsp':
        return BuildArtifactExtension.DOT_HSP;
      default:
        return BuildArtifactExtension.DOT_HAP;
    }
  }

  /**
   * 根据不同构建模式返回已经存在sourcemap的路径，供后续使用
   * @private
   */
  private getSourceMapSourcePath() {
    const defaultSourceMapPath = this.buildOption?.debuggable
      ? path.resolve(this.etsAssetsPath, BuildArtifactConst.SOURCEMAPS_MAP)
      : path.resolve(this.pathInfo.getModuleBuildCachePath(), `${this.targetName}@CompileArkTS`, 'esmodule', 'release', BuildArtifactConst.SOURCEMAPS_MAP);
    return defaultSourceMapPath;
  }

  /**
   * 根据构建模式将原有的sourceMap文件复制到intermediate对应目录下
   * @private
   */
  private generateInterSourceMap() {
    // 用户没有指定路径，sourceMap默认将生成在ets下，在此阶段复制一份到output下
    if (!this.sdkInfo.hasRollUpPluginInEtsLoader) {
      return;
    }
    // 不同模式 sourceMap的复制的源位置不一样
    const defaultSourceMapPath = this.getSourceMapSourcePath();
    if (fse.existsSync(defaultSourceMapPath)) {
      fse.ensureDirSync(this.sourceMapInterDirPath);
      fse.copyFileSync(defaultSourceMapPath, path.resolve(this.sourceMapInterDirPath, BuildArtifactConst.SOURCEMAPS_MAP));
    }
  }

  private moveSourceMap() {
    // 用户没有指定路径，sourceMap默认将生成在ets下，在此阶段复制一份到output下
    if (!this.sdkInfo.hasRollUpPluginInEtsLoader) {
      return;
    }
    // 优先从intermedia目录下读取，否则从sourceMap存在的源路径etsLoader里读取
    const defaultSourceMapPath = fse.existsSync(this.pathInfo.getSourceMapIntermediatePath())
      ? this.pathInfo.getSourceMapIntermediatePath()
      : this.getSourceMapSourcePath();
    if (fse.existsSync(defaultSourceMapPath)) {
      if (coreParameter.properties[OHOS_ARK_COMPILE_SOURCE_MAP_DIR]) {
        const sourceMapDirPath = this.getSourceMapPath(coreParameter.properties[OHOS_ARK_COMPILE_SOURCE_MAP_DIR]);
        fse.ensureDirSync(path.resolve(sourceMapDirPath, this.moduleName));
        fse.copyFileSync(defaultSourceMapPath, path.resolve(sourceMapDirPath, this.moduleName, BuildArtifactConst.SOURCEMAPS_MAP));
      } else {
        fse.ensureDirSync(this.pathInfo.getDefaultSourceMapPath());
        fse.copyFileSync(defaultSourceMapPath, path.resolve(this.pathInfo.getDefaultSourceMapPath(), BuildArtifactConst.SOURCEMAPS_MAP));
      }
    }
  }

  /**
   * 在 ohos-test-coverage 场景下打包前用测试框架生成的 ohosTestSourceMap.map 替换 loader_out 目录下的 sourceMaps.map 文件内容。
   * 因为覆盖率场景 rollup 生成的 sourceMap是不对的，测试框架自己生成的 sourceMap 是对的。
   */
  private async replaceSourceMapInCoverageMode() {
    if (!InjectUtil.isOhosTestCoverage()) {
      return;
    }
    const defaultSourceMapPath = path.join(this.etsAssetsPath, BuildArtifactConst.SOURCEMAPS_MAP);
    const ohosTestSourceMapPath = path.join(this.pathInfo.getModulePath(), '.test/default/intermediates/ohosTest/coverage_sourcemap/ohosTestSourceMap.map');
    await overWriteSourceMap(defaultSourceMapPath, ohosTestSourceMapPath);
  }

  initTaskDepends() {
    this.declareDependsList(MakePackInfo.name, CacheNativeLibs.name, GenerateMetadata.name);
    this.declareDepends(SyscapTransform.name);
    this.declareDepends(ProcessRouterMap.name);
    this.declareDepends(ProcessStartupConfig.name);
    const useRollupETS = fse.existsSync(path.resolve(this.sdkInfo.getEtsLoader(), ToolChainsConst.ROLL_UP_CONFIG_FILE));
    const useRollupJS = fse.existsSync(path.resolve(this.sdkInfo.getJsLoader(), ToolChainsConst.ROLL_UP_CONFIG_FILE));
    this._log.debug('initTaskDepends:', `moduleName=${this.moduleName}, taskName=${this.name}, useRollupETS=${useRollupETS}, useRollupJS=${useRollupJS}`);
    if (InjectUtil.isHotReload() || InjectUtil.isColdReload()) {
      if (useRollupETS) {
        this.declareDepends(InjectUtil.isHotReload() ? 'HotReloadArkTS' : 'ColdReloadArkTS');
      }
      if (useRollupJS) {
        this.declareDepends('CompileJS');
      } else {
        this.declareDepends('BuildJS');
      }
      return;
    }
    const prefix: string = this.targetName === ohosTestTargetName ? 'OhosTest' : '';

    // 处理ohosTest差异化场景
    if (this.targetName === ohosTestTargetName) {
      this.declareDepends(TaskNames.Task.OHOS_TEST_COPY_MOCK_CONFIG_JSON.name);
    }

    this.declareDepends(this.sdkInfo.hasRollUpPluginInEtsLoader ? `${prefix}CompileArkTS` : `${prefix}BuildArkTS`);
    this.declareDepends(this.sdkInfo.hasRollUpPluginInJsLoader ? `${prefix}CompileJS` : `${prefix}BuildJS`);
  }

  private getSourceMapPath(sourceMapPath: string) {
    return path.isAbsolute(sourceMapPath) ? sourceMapPath : path.resolve(this.projectModel.getProjectDir(), 'hvigor', sourceMapPath);
  }

  protected doDependenciesCheck() {
    if (this.targetService.isDebug()) {
      return;
    }
    const { harDependencies, hspDependencies, byteCodeHarDependencies } = this.collectDependencies();
    this.doNativeStripDependenciesCheck(harDependencies);
    this.doObfuscationDependenciesCheck(hspDependencies, byteCodeHarDependencies);
  }

  // 针对远程HAR，如果存在没有strip的.so，需要warning提示
  private doNativeStripDependenciesCheck(harDependencies: Dependency[]) {
    if (this.targetService.getBuildOption()?.nativeLib?.debugSymbol?.strip !== false) {
      return;
    }
    const noNativeStripPkgNames = harDependencies
      .filter((dependency) => {
        if (dependency.isLocal()) {
          return false;
        }
        const pkgJson = dependency.getPackageJsonObj() as any;
        return pkgJson.metadata?.nativeDebugSymbol === false;
      })
      .map((dependency) => dependency.getPackageName());

    if (!noNativeStripPkgNames.length) {
      return;
    }
    this._log.warn(`HAR dependencies [${noNativeStripPkgNames.join(', ')}] contain unstripped .so files. These .so files will not be stripped later.`);
  }

  private excludeObfuscatedByteCodeHarDependencies(byteCodeHarDependencies: Dependency[]) {
    const config = getJson5Obj(this.pathInfo.getByteCodeObfConfigPath());
    if (!config) {
      return byteCodeHarDependencies;
    }
    const skippedHars: Set<string> = new Set(config.skippedRemoteHarList || []);
    return byteCodeHarDependencies.filter(dependency => skippedHars.has(dependency.getDependencyName()));
  }

  // 针对依赖中的远程hsp和字节码HAR，如果存在为混淆的，需要做warning提示
  private doObfuscationDependenciesCheck(hspDependencies: Dependency[], byteCodeHarDependencies: Dependency[]) {
    const remoteHspDependencies = hspDependencies.filter((dependency) => !dependency.isLocal());
    const finalByteCodeHarDependencies = this.excludeObfuscatedByteCodeHarDependencies(byteCodeHarDependencies);
    if (!remoteHspDependencies.length && !finalByteCodeHarDependencies.length) {
      return;
    }
    let tips = '';
    if (finalByteCodeHarDependencies.length) {
      tips += `Bytecode HARs [${finalByteCodeHarDependencies.map((d) => d.getPackageName()).join(', ')}]`;
    }
    if (remoteHspDependencies.length) {
      tips += `${tips ? ' and ' : ''}HSPs [${remoteHspDependencies.map((d) => d.getPackageName()).join(', ')}]`;
    }
    this._log.warn(`${tips} to integrate are not obfuscated and will not be obfuscated later.`);
  }

  private collectDependencies() {
    const dependencyManager = new DependencyManager(this.projectModel.isFaMode(), this.moduleModel, this.projectModel);
    const { npmDependencies } = dependencyManager.collectAllDependencies();
    const hspDependencies: Dependency[] = [];
    const harDependencies: Dependency[] = [];
    const byteCodeHarDependencies: Dependency[] = [];
    npmDependencies.forEach((dependency) => {
      if (dependency.isByteCodeHarDependency()) {
        byteCodeHarDependencies.push(dependency);
      }
      const dependencyType = dependency.getDependencyType();
      if (dependencyType === DependencyType.DEPENDENCY_TYPE_HSP) {
        hspDependencies.push(dependency);
      } else if (dependencyType === DependencyType.DEPENDENCY_TYPE_HAR) {
        harDependencies.push(dependency);
      }
    });
    return { hspDependencies, harDependencies, byteCodeHarDependencies };
  }

  async beforeAlwaysAction(): Promise<void> {
    await super.beforeAlwaysAction();
    this.doDependenciesCheck();
    doObfuscationCheck(this.targetService, this._log);
    if (InjectUtil.isByteCodeHarOptimize()) {
      const importeesPath = this.getImporteesCachePath(DIRECT_IMPORTEE_CACHE_FILE_NAME, 'CompileArkTS');
      this.doDevDependencyCheck(importeesPath);
    }
  }

  protected async copyIntermediates (): Promise<void> {
    // create fastapp dir
    fse.mkdirsSync(this.fastAppDirPath);
    const tasks = [];
    const etsPath = path.resolve(this.fastAppDirPath, CommonConst.ETS);
    const resourcesPath = path.resolve(this.fastAppDirPath, CommonConst.RESOURCES_PATH);
    const bundleName  = getAppInfoByProject(this.service.getProjectModel()).bundleName;

    // intermediates\\loader_out\\default目录下的ets
    tasks.push(fse.copy(this.etsAssetsPath, etsPath));
    // tasks.push(fse.copyFile(this.packageHapJsonPath, path.resolve(this.fastAppDirPath, CommonConst.MODULE_JSON)));
    tasks.push(fse.copy(this.resourcePath, resourcesPath));
    // intermediates\\res\\default目录下的resources.index
    tasks.push(fse.copyFile(this.indexPath, path.resolve(this.fastAppDirPath, BuildArtifactConst.RESOURCE_INDEX)));
    // intermediates\\package\\default目录下的pkgContextInfo.json
    tasks.push(fse.copyFile(this.pkgContextInfoPath, path.resolve(this.fastAppDirPath, CommonConst.PKG_CONTEXT_INFO_JSON)));

    // intermediates\\package\\default目录下的module.json
    const copyModuleJSONFile = this.copyJSONFile(this.packageHapJsonPath, path.resolve(this.fastAppDirPath, CommonConst.MODULE_JSON), (moduleJSON) => {
      moduleJSON.app.bundleName = bundleName;
      return moduleJSON;
    });
    // outputs\\default目录下的pack.info
    const copyPackInfoFile = this.copyJSONFile(this.allPackInfoPath[0], path.resolve(this.fastAppDirPath, BuildArtifactConst.PACK_INFO), (packInfo) => {
      packInfo.summary.app.bundleName = bundleName;
      return packInfo;
    });

    tasks.push(copyModuleJSONFile);
    tasks.push(copyPackInfoFile);

    await Promise.all(tasks);
  }

  private async copyJSONFile(sourcePath: string, targetPath: string, setter: (jsonObject: any) =>  any): Promise<void> {
    return fse.readFile(sourcePath).then((content) => {
      let packInfo = JSON.parse(content.toString());

      packInfo = setter(packInfo);
      return fse.writeFile(targetPath, JSON.stringify(packInfo));
    }).catch((err) => {
      this._log.debug('cannot copy intermediate file', err);
    });
  }
}

export const packageHapDepends = (service: TargetTaskService) => {
  const depends: string[] = [MakePackInfo.name, CacheNativeLibs.name, GenerateMetadata.name];
  const targetData = service.getTargetData();
  const sdkInfo = service.getSdkInfo();
  depends.push(SyscapTransform.name);
  depends.push(ProcessRouterMap.name);

  if (InjectUtil.isHotReload() || InjectUtil.isColdReload()) {
    if (sdkInfo.hasRollUpPluginInEtsLoader) {
      depends.push(InjectUtil.isHotReload() ? 'HotReloadArkTS' : 'ColdReloadArkTS');
    }
    depends.push(sdkInfo.hasRollUpPluginInJsLoader ? 'CompileJS' : 'BuildJS');
    return depends;
  }
  if (targetData.getTargetName() === ohosTestTargetName) {
    depends.push(TaskNames.Task.OHOS_TEST_COPY_MOCK_CONFIG_JSON.name);
  }

  if (service.verifyModuleVersionIsArkTS12()) {
    if (targetData.getTargetName() === 'ohosTest') {
      depends.push(TaskNames.Task.OHOS_TEST_COMPILE_ARKTS_EVOLUTION.name);
    } else {
      depends.push(TaskNames.Task.COMPILE_ARKTS_EVOLUTION.name);
    }
  } else {
    const prefix: string = targetData.getTargetName() === ohosTestTargetName ? 'OhosTest' : '';
    depends.push(sdkInfo.hasRollUpPluginInEtsLoader ? `${prefix}CompileArkTS` : `${prefix}BuildArkTS`);
    depends.push(sdkInfo.hasRollUpPluginInJsLoader ? `${prefix}CompileJS` : `${prefix}BuildJS`);
  }
  depends.push(ProcessIntegratedHsp.name);
  return depends;
};